﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Web;
using System.Text.RegularExpressions;

namespace DiskFileUpload
{
    public class MultipartStream : Stream
    {
        private System.Text.Encoding bodyEncoding = System.Text.Encoding.UTF8;

        private static int HEADER_PART_SIZE_MAX = 4096;

        private static byte[] HEADER_SEPARATOR = new byte[] { 0xd, 0xa, 0xd, 0xa };

        // 匹配参数名称的正则表达式 . 表示除换行之外的所有字符
        // +? 表示懒惰模式
        private static readonly System.Text.RegularExpressions.Regex nameRegex
            = new System.Text.RegularExpressions.Regex("name=\"(.+?)\"");

        // 匹配上传文件名的正则表达式
        private static readonly System.Text.RegularExpressions.Regex filenameRegex
            = new System.Text.RegularExpressions.Regex("filename=\"(.+)\"");

        private static readonly System.Text.RegularExpressions.Regex contentTypeRegex
            = new Regex("Content-Type=\"(.+)\"");

        private byte[] boundary;
        private int boundaryLength;

        int[] next;

        // 流中内容的字节长度
        private long length;
        public override long Length
        {
            get { return this.length; }
        }

        // 当前位置
        public override long Position
        {
            get { return this.startIndex + this.bufferStart; }
            set { throw new Exception("不允许设置当前位置"); }
        }

        // 内部预读的缓冲区
        private byte[] buffer;

        // 缓冲内容的起始位置
        private int startIndex;
        private int bufferLength;

        // 缓冲区也不一定填满，缓冲区有内容部分的起始下标
        private int bufferStart;
        private int bufferEnd;

        private IFileUpload worker;

        #region KMP

        /// <summary>
        /// 构造 KMP 的匹配模式数组
        /// 对于 KMP 来说，函数表示包括当前字符的字串与整个模式串匹配的数量
        /// 0 表示当前位置无匹配
        /// 
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        private int[] BuildKMP(byte[] pattern)
        {
            int[] next = new int[pattern.Length];

            next[0] = 0;        // 第一个位置一定为 0

            int j = 0;          // 匹配的起始位置
            for (int i = 1; i < pattern.Length; i++)
            {
                // 如果已经匹配上，但是现在不能匹配，回溯寻找
                while (j > 0 && pattern[j] != pattern[i])
                {
                    j = next[j - 1];
                }

                // 如果能够匹配上，向下推进一个位置
                // 注意 i 在 for 循环中自动推进
                if (pattern[j] == pattern[i])
                    j++;

                // 保存
                next[i] = j;
            }
            return next;
        }

        #endregion

        public MultipartStream(IFileUpload worker, string boundaryString, int bufferSize)
        {
            boundaryString = "\r\n--" + boundaryString;
            this.boundary = System.Text.Encoding.ASCII.GetBytes(boundaryString);
            this.boundaryLength = boundary.Length;
            this.next = this.BuildKMP(this.boundary);

            this.worker = worker;

            // 设置流的总长度
            this.length = worker.Length;

            // 预读
            this.startIndex = 0;

            // 缓冲区大小
            this.bufferLength =
                System.Math.Max(worker.PreloadLength, 64 * 1024);

            this.buffer = new byte[this.bufferLength];
            this.bufferStart = 0;
            this.bufferEnd = worker.GetPreloadedBody(this.buffer, 0);
        }

        // 是否允许读取
        public override bool CanRead
        {
            get { return true; }
        }

        // 是否允许定位
        public override bool CanSeek
        {
            get { return false; }
        }

        // 是否允许写入
        public override bool CanWrite
        {
            get { return false; }
        }

        public override void Flush()
        {
            throw new NotImplementedException();
        }

        public override int ReadByte()
        {
            // 是否已经读取到了最后
            if (this.startIndex + this.bufferStart >= this.length)
                return -1;

            // 希望读取的内容是否在缓冲区中
            if (this.bufferStart == this.bufferEnd)
            {
                // 已经超出边界，必须继续进行读取
                int count = this.MakeAvailable();
            }
            int result = this.buffer[this.bufferStart];
            this.bufferStart++;

            return result;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (count == 0)
                return 0;

            int res = this.Available();
            if (res == 0)
            {
                res = this.MakeAvailable();
                if (res == 0)
                {
                    return -1;
                }
            }

            res = System.Math.Min(count, res);
            System.Buffer.BlockCopy(
                this.buffer, this.bufferStart,
                buffer,
                0,
                res
                );

            // 修正指针
            this.bufferStart += res;

            return res;
        }

        // 检查缓冲区中还有多少数据可读
        // BufferStart 当前开始的地方
        // BufferEnd 结束的地方
        private int Available()
        {
            int count = this.bufferEnd - this.bufferStart;
            return count;
        }


        // 在缓冲区中，从当前位置开始，进行边界匹配。
        // 如果边界已经在缓冲区中，返回边界的
        private int FindSeparator()
        {
            int j = 0;
            for (int i = this.bufferStart; i < this.bufferEnd; i++)
            {
                byte result = this.buffer[i];
                /// KMP
                while (j > 0 && result != this.boundary[j])
                    j = this.next[j - 1];
                if (result == this.boundary[j])
                    j++;
                if (j == this.boundaryLength)
                {
                    // 已经完全匹配， 返回边界的位置
                    return i - this.boundaryLength + 1;
                }
            }

            // 如果没有完全匹配，返回 -1
            return -1;
        }

        private int MakeAvailable()
        {
            // 当前缓冲区中的指针已经在起始位置，则不用读取。
            if (this.bufferStart == 0)
                return 0;

            // 如果不在起始位置, 那么将已经读取的数据丢弃掉
            // 将剩下的数据移动到缓冲区的前面，再读一次数据填充后继的空间
            int remain = this.bufferEnd - this.bufferStart;
            Array.Copy(this.buffer, this.bufferStart,
                this.buffer, 0,
                remain
                );

            this.startIndex += this.bufferStart;
            this.bufferStart = 0;
            this.bufferEnd = remain;

            // 重新读取一次数据
            for (; ; )
            {
                int bytes = this.worker.Read(buffer, this.bufferEnd, this.bufferLength - this.bufferEnd);

                if (bytes == 0)
                {
                    return 0;
                }

                this.bufferEnd += bytes;

                int av = Available();

                if (av > 0)
                {
                    return av;
                }
            }

        }

        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        #region 读取数据

        // 读取边界之后，后继的字节为 \r\n 表示有数据可读
        // 如果为 --, 表示已经是最后一个边界
        // 其它情况说明读取出现错误
        public bool ReadBoundary()
        {
            byte a;
            byte b;
            bool nextChunk = false;

            if (this.bufferEnd - this.bufferStart < this.boundaryLength)
                this.MakeAvailable();

            this.bufferStart += this.boundaryLength;    // 跳过分隔界线符
            try
            {
                a = (byte)this.ReadByte();
                b = (byte)this.ReadByte();

                if (a == '-' && b == '-')
                {
                    nextChunk = false;
                }
                else if (a == '\r' && b == '\n')
                {
                    nextChunk = true;
                }
                else
                {
                    // 如果读到的既不是回车换行，又不是两个减号，
                    // 说明输入流有问题，则抛出异常。
                    throw new System.InvalidOperationException(
                                    " Unexpected characters follow a boundary ");
                }
            }
            catch (IOException e)
            {
                throw new InvalidOperationException("Stream ended unexpectedly ");
            }
            return nextChunk;
        }



        public bool SkipPreamble()
        {
            //修改成员变量boundary和boundaryLength的值
            System.Buffer.BlockCopy(
                boundary, 2,
                boundary, 0,
                boundary.Length - 2);

            this.boundaryLength = this.boundaryLength - 2;
            try
            {
                //丢掉第一个分隔界线符前的注释数据
                discardBodyData();
                return this.ReadBoundary();
            }
            catch (Exception e)
            {
                return false;
            }
            finally
            {
                //恢复成员变量boundary和boundaryLength的值
                System.Buffer.BlockCopy(
                    boundary, 0,
                    boundary, 2,
                    boundary.Length - 2
                    );

                boundaryLength = boundary.Length;
                boundary[0] = 0x0D; //‘\n’
                boundary[1] = 0x0A; // ‘\r’
            }
        }

        private void discardBodyData()
        {
        }

        public FileItem ReadHeader()
        {
            string header = this.ReadHeaderString();

            Match nameMatch = nameRegex.Match(header);
            string name = nameMatch.Groups[1].Value;

            Match filenameMatch = filenameRegex.Match(header);

            bool isForm = true;
            string filepath = string.Empty;
            string contentType = string.Empty;

            if (filenameMatch.Success)
            {
                isForm = false;
                filepath = filenameMatch.Groups[1].Value;

                Match contentTypeMatch = contentTypeRegex.Match(header);
                contentType = contentTypeMatch.Groups[1].Value;
            }

            FileItem item = new FileItem(
                name,
                isForm,
                filepath,
                contentType
                );
            return item;


        }

        // 从当前位置开始读取头部信息
        public string ReadHeaderString()
        {
            int i = 0;
            //从下面的代码看来，这里定义成一个byte即可，不用定义成byte数组
            byte[] b = new byte[1];
            //用于临时保存描述头信息的字节数组输出流
            System.IO.MemoryStream baos = new MemoryStream();

            //对描述头部分的数据内容过大进行限制处理            
            int sizeMax = HEADER_PART_SIZE_MAX;
            int size = 0;
            while (i < 4)
            {
                try
                {
                    b[0] = (byte)this.ReadByte();
                }
                catch (IOException e)
                {
                    throw new System.InvalidOperationException("Stream ended unexpectedly ");
                }
                size++;
                //静态常量HEADER_SEPARATOR的值为：{0x0D, 0x0A, 0x0D, 0x0A}
                if (b[0] == HEADER_SEPARATOR[i])
                {
                    i++;
                }
                else
                {
                    i = 0;
                }
                if (size <= sizeMax)
                {
                    baos.WriteByte(b[0]); // 将当前字节存入缓冲流
                }
            }
            String headers = null; // 找到HEADER_SEPARATOR后，获取描述头

            headers = System.Text.Encoding.ASCII.GetString(baos.ToArray());

            return headers;
        }

        public int ReadBodyData(FileItem item)
        {
            int count;
            if (item.IsFormField)
            {
                item.Value = this.ReadBodyText();
                count = item.Value.Length;
            }
            else
            {
                count = this.ReadBodyData(item.Stream);
            }

            return count;
        }

        public string ReadBodyText()
        {
            using (System.IO.MemoryStream ms = new MemoryStream())
            {
                this.ReadBodyData(ms);
                string body = this.worker.Encoding.GetString(ms.ToArray());
                return body;
            }
        }

        public int ReadBodyData(System.IO.Stream output)
        {
            // 用于控制循环的变量
            bool done = false;
            int pos;

            // 写入到输出流中的字节个数
            int total = 0;
            while (!done)
            {
                pos = this.FindSeparator();// 搜索分隔界线
                if (pos != -1) //缓冲区buffer中包含有分隔界线
                {
                    int count = pos - this.bufferStart;
                    output.Write(this.buffer, this.bufferStart, count);
                    this.bufferStart += count;

                    total += count;
                    done = true;// 跳出循环
                }
                else //缓冲区buffer中没有包含分隔界线
                {
                    // 保留当前匹配的字符个数
                    int count = this.bufferEnd - this.bufferStart - this.boundaryLength;

                    output.Write(this.buffer, this.bufferStart, count);
                    this.bufferStart += count;
                    this.MakeAvailable();

                    total += count;
                }
            }
            output.Flush();
            return total;
        }

        // 按照MIME规范，消息头和消息体之间的分隔界线前面可以有一些作为注释信息的内容，
        // discardBodyData就是为了抛弃这些注释信息而提供的。
        public int DiscardBodyData()
        {
            return -1;
        }

        #endregion
    }
}
